"""
@author: 景云鹏
@email: 310491287@qq.com
@date: 2022/4/28

本文件中函数参考：
http://www-optima.amp.i.kyoto-u.ac.jp/member/student/hedar/Hedar_files/TestGO_files/Page364.htm
以及
http://www.sfu.ca/~ssurjano/optimization.html
"""

from numpy import *

from benchmarks.function import Function


@Function(intervals=[-5.12, 5.12])
def ackley(x):
    n = len(x)
    a = 20
    b = 0.2
    c = 2 * pi
    s1 = s2 = 0
    for i in range(n):
        s1 += x[i] * x[i]
        s2 += cos(c * x[i])

    return -a * exp(-b * sqrt(1 / n * s1)) - exp(1 / n * s2) + a + exp(1)


@Function(dim=2, intervals=[-4.5, 4.5])
def beale(x):
    a = [1.5, 2.25, 2.625]
    s = 0
    for i in range(3):
        s += (a[i] - x[0] * (1 - x[1] ** (i + 1))) ** 2
    return s


@Function(dim=2, intervals=[-100, 100])
def boh1(x):
    return x[0] ** 2 + 2 * x[1] ** 2 - 0.3 * cos(3 * pi * x[0]) - 0.4 * cos(4 * pi * x[1]) + 0.7


@Function(dim=2, intervals=[-100, 100])
def boh2(x):
    return x[0] ** 2 + 2 * x[1] ** 2 - 0.3 * cos(3 * pi * x[0]) * cos(4 * pi * x[1]) + 0.3


@Function(dim=2, intervals=[-100, 100])
def boh3(x):
    return x[0] ** 2 + 2 * x[1] ** 2 - 0.3 * cos(3 * pi * x[0] + 4 * pi * x[1]) + 0.3


@Function(dim=2, intervals=[-10, 10])
def booth(x):
    return (x[0] + 2 * x[1] - 7) ** 2 + (2 * x[0] + x[1] - 5) ** 2


@Function(dim=2, min_value=0.397887, intervals=[[-5, 10], [0, 15]])
def branin(x):
    pii = 1.0 / pi
    first = (x[1] - 5 * pii * x[0] * (0.25 * pii * x[0] - 1) - 6) ** 2
    second = 10 * (1 - 0.125 * pii) * cos(x[0])
    return first + second + 10


@Function(dim=4, intervals=[-10, 10])
def colville(x):
    first = 100 * (x[0] ** 2 - x[1]) ** 2
    second = (x[0] - 1) ** 2 + (x[2] - 1) ** 2
    third = 90 * (x[2] ** 2 - x[3]) ** 2
    fourth = 10 * (x[1] - 1) ** 2 + (x[3]) ** 2
    fifth = 19.8 * (x[1] - 1) * (x[3] - 1)
    return first + second + third + fourth + fifth


@Function(intervals=[-10, 10])
def dp(x):
    n = len(x)
    s1 = 0
    for i in range(1, n):
        s1 += (i + 1) * (2 * x[i] ** 2 - x[i - 1]) ** 2
    return (x[0] - 1) ** 2 + s1


@Function(dim=2, min_value=-1, intervals=[-100, 100])
def easom(x):
    index = (x[0] - pi) ** 2 + (x[1] - pi) ** 2
    return -cos(x[0]) * cos(x[1]) * exp(-index)


@Function(dim=2, min_value=3, intervals=[-2, 2])
def gold(x):
    a = x[0] + x[1] + 1
    b = 19 - 14 * x[0] + 3 * x[0] ** 2 - 14 * x[1] + 6 * x[0] * x[1] + 3 * x[1] ** 2
    c = 2 * x[0] - 3 * x[1]
    d = 18 - 32 * x[0] + 12 * x[0] ** 2 + 48 * x[1] - 36 * x[0] * x[1] + 27 * x[1] ** 2
    return (1 + a * a * b) * (30 + c * c * d)


@Function(intervals=[-600, 600])
def griewank(x):
    s = 0
    p = 1
    for i, xi in enumerate(x):
        s += xi * xi
        p *= cos(xi / sqrt(i + 1))
    return s / 4000 - p + 1


# TODO 太复杂，跳过 def hartmann

@Function(dim=2, intervals=[-5, 5])
def hump(x):
    return 1.0316285 + 4 * x[0] ** 2 - 2.1 * x[0] ** 4 + x[0] ** 6 / 3 + x[0] * x[1] - 4 * x[1] ** 2 + 4 * x[1] ** 4


@Function(intervals=[-10, 10])
def levy(x):
    s = 0
    n = len(x)
    for i in range(n):
        z = 1 + (x[i] - 1) / 4
        if i == 0:
            s += sin(pi * z) ** 2
        if i != n - 1:
            s += (z - 1) ** 2 * (1 + 10 * sin(pi * z + 1)) ** 2
        else:
            s += (z - 1) ** 2 * (1 + sin((2 * pi * z)) ** 2)
    return s


@Function(dim=2, intervals=[-10, 10])
def matyas(x):
    return 0.26 * (x[0] ** 2 + x[1] ** 2) - 0.48 * x[0] * x[1]


# TODO: 不同维度的最小值不同，暂时简化化简维度为2,x*=[2.20,1.57]
@Function(dim=2, min_value=-1.8013, intervals=[0, pi])
def mich(x):
    s = 0
    for i, xi in enumerate(x):
        s -= sin(xi) * (sin((i + 1) * xi ** 2 / pi)) ** 20
    return s


@Function(intervals=[-20, 20])
def perm(x):
    n = len(x)
    s = 0
    for i in range(n):
        si = 0
        for j in range(n):
            si += (j + 11) * (x[j] ** i - 1 / (j + 1) ** i)
        s += si ** 2
    return s


# TODO 维度是4的倍数
@Function(dim=4, intervals=[-4, 5])
def powell(x):
    s = 0
    for i in range(len(x) // 4):
        x0, x1, x2, x3 = x[4 * i:4 * i + 4]
        s += (x0 + 10 * x1) ** 2
        s += 5 * (x2 - x3) ** 2
        s += (x1 - 2 * x2) ** 4
        s += 10 * (x0 - x3) ** 4
    return s


@Function(intervals=[-100, 100])
def power(x):
    n = len(x)
    s = 0
    for i in range(n):
        si = 0
        for j in range(n):
            si += x[j] ** (i + 1)
            si -= j ** (i + 1)
        s += si ** 2
    return s


@Function(intervals=[-5.12, 5.12])
def rast(x):
    n = len(x)
    s = 10 * n
    for i in range(n):
        s += x[i] ** 2 - 10 * cos((2 * pi * x[i]))
    return s


@Function(intervals=[-5, 10])
def rosen(x):
    n = len(x)
    s = 0
    for i in range(n - 1):
        s += 100 * (x[i] ** 2 - x[i + 1] ** 2) + (x[i] - 1) ** 2
    return s


@Function(intervals=[-500, 500])
def schw(x):
    x = asarray(x)
    return 418.9829 * len(x) - sum(sin(sqrt(abs(x))) * x)


@Function(dim=4, min_value=-10.5363, intervals=[0, 10])
def shekel(x):
    s = 0
    c = asarray([
        [4, 1, 8, 6, 3, 2, 5, 8, 6, 7],
        [4, 1, 8, 6, 7, 9, 3, 1, 2, 3.6],
        [4, 1, 8, 6, 3, 2, 5, 8, 6, 7],
        [4, 1, 8, 6, 7, 9, 3, 1, 2, 3.6],
    ])
    b = asarray([1, 2, 2, 4, 4, 6, 3, 7, 5, 5]) / 10
    for i in range(10):
        si = 0
        for j in range(4):
            si += (x[j] - c[j][i]) ** 2
        s -= 1 / (si + b[i])
    return s


@Function(dim=2, min_value=-186.7309, intervals=[-10, 10])
def shub(x):
    s1 = 0
    s2 = 0
    for i in range(5):
        s1 += (i + 1) * cos((i + 2) * x[0] + i + 1)
        s2 += (i + 1) * cos((i + 2) * x[1] + i + 1)
    return s1 * s2


@Function(intervals=[-5.12, 5.12])
def sphere(x):
    return sum(asarray(x) ** 2)


@Function(intervals=[-5.12, 5.12])
def sum_squares(x):
    s = 0
    for i, xi in enumerate(x):
        s += (i + 1) * xi ** 2
    return s


@Function()
def trid(x):
    n = len(x)
    s = 0
    for xi in x:
        s += (xi - 1) ** 2
    for i in range(n - 1):
        s -= x[i] * x[i + 1]
    s += n * (n + 4) * (n - 1) / 6
    return s


@Function()
def zakh(x):
    s1 = s2 = 0
    for i, xi in enumerate(x):
        s1 += xi ** 2
        s2 += 0.5 * (i + 1) * xi
    return s1 + s2 ** 2 + s2 ** 4


if __name__ == '__main__':
    easom.figure()
